#include "spk_camera.h"
#include "config.h"
#include "utils.h"
#include "device_scan.h"
#include "device_ctrl.h"

// #pragma warning(push)
// #pragma warning(disable : 4244)
// #pragma warning(disable : 26451)
// #pragma warning(disable : 6386)

#define LOG_TAG "device_scan"
#define LOG_LVL LOG_LVL_INFO
#include "spk_ulog.h"

cls_device_scan::cls_device_scan(cls_spkcam *p_app)
{
    app = p_app;
    cfg_src = app->cfg_src;
    cfg_cur = app->cfg_cur;

    count = 0;
    fd = udp_init(DEVICE_SCAN_BROADCAST_PORT);

    rx_thread = nullptr;
    ctrl = nullptr;

    my_ip_addr = ip_addr_trans(cfg_src->ip_addr);
    name = string((char *)cfg_src->device_name);

    if (fd > 0)
    {
        rx_thread = new std::thread(cls_device_scan::rx_packet_thread, this);
        // rx_packet_thread(this);
    }
}

cls_device_scan::~cls_device_scan()
{
    count = 0;
    if (fd > 0)
        closesocket(fd);
    fd = -1;

    if (ctrl)
    {
        delete ctrl;
        ctrl = nullptr;
    }
}

int cls_device_scan::scan(void)
{
    struct in_addr var_ip;

    /*发送内容为PC端IP地址*/
    inet_pton(AF_INET, my_ip_addr.c_str(), (void *)&var_ip);

    /*发送目标为广播地址*/
    // 如果 my_ip_addr 是 "192.168.1.1"，经过这段代码处理后，ip 将变为 "192.168.1.255"，这就是该子网的广播地址
    size_t pos = my_ip_addr.find_last_of('.');
    string ip = my_ip_addr.substr(0, pos);
    ip = ip + std::string(".255");

    return send_packet(CMD_DEVICE_SCAN, &var_ip, 4, &ip);
}

void cls_device_scan::rx_packet_thread(void *arg)
{
    cls_device_scan *ins = (cls_device_scan *)arg;
    spk_packet::recv_packet_thread_ex(ins->io_read, ins->on_rx_packet, ins);
}

int cls_device_scan::udp_init(int port)
{
    int sock = (int)::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    if (sock < 0)
    {
        LOG_E("socket创建失败!");
        return -1;
    }

    if (port > 0)
    {
        REUSE_PORT(sock)
        struct sockaddr_in addr;
        memset(&addr, 0, sizeof(addr));

        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = INADDR_ANY;
        addr.sin_port = htons(port);

        int err = ::bind(sock, (struct sockaddr *)&addr, sizeof(addr));

        if (err < 0)
        {
            LOG_E("socket绑定失败!");
            closesocket(sock);
            return -1;
        }
    }
    return sock;
}

int cls_device_scan::io_write(void *data, int len, void *param)
{
    if (fd < 0)
        return fd;

    count = 0;

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));

    string ip = *(string *)param;
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr(ip.c_str());
    addr.sin_port = htons(DEVICE_SCAN_BROADCAST_PORT);

    int ret = sendto(fd, (char *)data, len, 0, (struct sockaddr *)&addr, sizeof(addr));

    if (ret < 0)
    {
        LOG_E("socket发送失败!");
        return -1;
    }

    return ret;
}

void cls_device_scan::on_rx_packet(void *rx_buffer, int len, u8 type, void *param)
{
    cls_device_scan *ins = (cls_device_scan *)param;

    switch (type)
    {
        /*收到设备发来的信息*/
    case CMD_DEVICE_INFO:
#ifdef _WIN32
        if (ip_addr_trans((u8 *)rx_buffer) != ins->my_ip_addr)
        {
            ::SendMessage(ins->notify_win, MSG_DEVICE_INFO, (WPARAM)&ins->count, (LPARAM)(rx_buffer));
            ins->count++;
        }
#endif
        break;
        /*收到来自于上位机的扫描请求信息*/
    case CMD_DEVICE_SCAN:
    {
        u8 my_ip[4];
        u8 pc_ip[4];
        int len = 0;
        char *buffer = nullptr;
        memcpy(pc_ip, rx_buffer, 4);

        LOG_D("recv scan msg from(%u.%u.%u.%u)", pc_ip[0], pc_ip[1], pc_ip[2], pc_ip[3]);

        ip_addr_trans(ins->my_ip_addr, my_ip);
        len = ins->name.length() + 1 + 4;
        buffer = new char[len];
        memset(buffer, 0, len);
        memcpy(buffer, my_ip, 4);                                  /*前4个字节为IP地址*/
        memcpy(buffer + 4, ins->name.c_str(), ins->name.length()); /*后面跟着设备名称*/

        string ip = ip_addr_trans(pc_ip);
        int ret = ins->send_packet(CMD_DEVICE_INFO, buffer, len, &ip); // 把自己的信息发送给上位机

        if (ret == len + 6)
        {
            if (ins->ctrl == nullptr)
            {
                /*检测到PC，创建TCP服务器接收PC端控制指令*/
                ins->ctrl = new cls_device_ctrl;
                ins->ctrl->run();
            }
        }
        delete[] buffer;
    }
    break;
    }
}

int cls_device_scan::io_read(void *data, int len, int time_out_ms, void *param)
{
    struct sockaddr_in addr;
    socklen_t addr_len = sizeof(addr);
    int sock = ((cls_device_scan *)param)->fd;
    int _len = recvfrom(sock, (char *)data, len, 0, (struct sockaddr *)&addr, &addr_len);
    return _len;
}
